using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using YDT_ABPvNext_Projects.EntityFrameworkCore;
using YDT_ABPvNext_Projects.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
using Microsoft.OpenApi.Models;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Swashbuckle;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using AbpVnext_Module.ExceptionFillter;
using Microsoft.AspNetCore.Mvc;
using AbpVnext_Module.Controllers;
using Volo.Abp.AspNetCore.ExceptionHandling;
using System.Net;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Identity;
using Volo.Abp.TenantManagement;
using Volo.Abp.AuditLogging;

namespace YDT_ABPvNext_Projects;

[DependsOn(
    typeof(YDT_ABPvNext_ProjectsHttpApiModule),
    typeof(AbpAutofacModule),
    typeof(AbpAspNetCoreMultiTenancyModule),
    typeof(YDT_ABPvNext_ProjectsApplicationModule),
    typeof(YDT_ABPvNext_ProjectsEntityFrameworkCoreModule),
    typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
    typeof(AbpAccountWebModule),
    typeof(AbpAspNetCoreSerilogModule),
    typeof(AbpSwashbuckleModule)
)]
public class YDT_ABPvNext_ProjectsHttpApiHostModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        //PreConfigure<OpenIddictBuilder>(builder =>
        //{
        //    builder.AddValidation(options =>
        //    {
        //        options.AddAudiences("YDT_ABPvNext_Projects");
        //        options.UseLocalServer();
        //        options.UseAspNetCore();
        //    });
        //});
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();
        var hostingEnvironment = context.Services.GetHostingEnvironment();

        //ConfigureAuthentication(context);
        //ConfigureBundles();
        //ConfigureUrls(configuration);
        //ConfigureConventionalControllers();
        //ConfigureVirtualFileSystem(context);
        //ConfigureCors(context, configuration);
        //ConfigureSwaggerServices(context, configuration);

        IServiceCollection services = context.Services;
        // Add services to the container.
        services.AddControllers(option =>
        {
            //添加自定义过滤器
            option.Filters.Add<CustomExceptionFilter>();
            //全局取消默认token校验 忽略xss攻击
            option.Filters.Add(new IgnoreAntiforgeryTokenAttribute());
        });
        //servicesout configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        services.AddEndpointsApiExplorer();

        ////配置日志工厂
        //services.AddLogging(x =>
        //{
        //    x.AddConfiguration(services.GetConfiguration().GetSection("Logging"));
        //    x.AddLog4Net("log4Net.config");
        //    x.AddConsole();
        //});

        //IConfiguration configuration = services.GetConfiguration();
        #region 身份验证模块
        //设置  Cookie 身份认证
        //在 ASP.NET Core 中，当你使用 AddAuthentication().AddCookie() 添加 Cookie 认证时，并没有直接指定一个具体的加密算法用于加密 Cookie 中的数据。
        //相反，ASP.NET Core 使用了 .NET Core 提供的 Data Protection API 来处理敏感数据的保护，这包括加密和签名。
        //Data Protection API 使用的是一种基于密钥的加密机制，但具体的加密算法（如 AES、RSA 等）是抽象化的，并且可能会随着.NET Core 版本的更新而更改。
        //Data Protection API 的目标是提供一个易于使用的 API，允许开发人员加密和验证数据，而无需深入了解底层加密机制。
        //context.Services.AddAuthentication(option =>
        //{
        //    //配置jwt认证方案
        //    //var deault = option.DefaultAuthenticateScheme;
        //    //var defalut = option.DefaultChallengeScheme;
        //}).AddCookie(option =>
        //{
        //    //option.Cookie.Name = "test";
        //    //option.ExpireTimeSpan = TimeSpan.FromMinutes(60);
        //});

        //微软默认身份认证 cookie认证 
        //应用身份认证模块volo..Identity.AspNetCore 、Volo.Abp.Account.Web 集成了AddAuthentication 身份认证
        // context.Services.AddAuthentication().AddCookie();

        //配置 jwt 身份认证
        //string? jwtSecurity = configuration.GetSection("configuration").Value;
        //services.AddAuthentication(option =>
        //{
        //    //配置jwt认证方案
        //    var deault = option.DefaultAuthenticateScheme;
        //    option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        //    var defalut = option.DefaultChallengeScheme;
        //    option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        //}).AddJwtBearer(option =>
        //{
        //    //配置token认证方式  设置哪些字段为true 代表验证哪些字段
        //    option.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
        //    {
        //        ValidateIssuer = true, //认证签发者 。生产token系统
        //        ValidateAudience = true,//认证受用者 erp系统
        //                                //ValidateLifetime = true,//认证生成token时间
        //        ValidateIssuerSigningKey = true,
        //        ValidIssuer = "jwt", //认证签发者 。生产token系统
        //        ValidAudience = "BasicModuleWebApiJWTAuthorization", //认证受用者 erp系统
        //        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecurity ?? "R2XMnd6TQdYRUmR0cCfL9fk651Fb3kDI")) //认证生成token密钥
        //    };
        //});

        //设置权限
        //Volo.Abp.Authorization 集成了授权模块 
        //context.Services.AddAuthorization(); 

        //微软自带身份认证
        //AddIdentity方法的参数类型指定AppUser类和IdentityRole类
        //AddEntityFrameworkStores方法指定Identity使用EF Core作为存储和项目中使用AppIdentityContext作为DB Context
        //context.Services.AddIdentity<Microsoft.AspNetCore.Identity.IdentityUser, Microsoft.AspNetCore.Identity.IdentityRole>()
        //    .AddEntityFrameworkStores<AppDbContext>().AddDefaultTokenProviders();
        #endregion

        #region swagger 模块
        //不带身份验证注册
        //services.AddSwaggerGen();
        //带身份验证注册
        //services.AddAbpSwaggerGen(option =>
        //{
        //    //添加token验证
        //    option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        //    {
        //        Description = "请输入token,格式为 Bearer xxxxxxxx",
        //        Name = "Authorization", //标头名
        //        In = ParameterLocation.Header,  //表示头部
        //        Type = SecuritySchemeType.ApiKey,
        //        BearerFormat = "JWT",  //token
        //        Scheme = "Bearer"
        //    });
        //    //添加验证条件
        //    option.AddSecurityRequirement(new OpenApiSecurityRequirement
        //    {
        //          {
        //            //Id = "Bearer" 表示引用的安全方案的 ID 是 "Bearer"，这个 ID 必须与之前通过 AddSecurityDefinition 定义的安全方案一致。
        //            //这部分代码的作用是告诉 Swagger 使用之前定义的 "Bearer" 安全方案。
        //            new OpenApiSecurityScheme{ Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id ="Bearer" } },
        //            new string[]{ }//不设权限
        //          }
        //    });
        //    option.SwaggerDoc("v3",
        //        new OpenApiInfo
        //        {
        //            Title = typeof(HostMoudule).Assembly.GetName().Name + "哈哈哈", //组名字，右上角可以选择
        //            Version = "v1",//版本
        //            Description = $"接口文档说明，放这里 ",
        //            Contact = new OpenApiContact()
        //            {
        //                Name = "zhangsan",//名字
        //                Email = "xxx@qq.com",//邮箱
        //                Url = null
        //            }//通常这里都是放联系，地址的，一般不用
        //        });
        //    //显示Abp自动api接口
        //    option.DocInclusionPredicate((x, y) => true);
        //    //option.CustomSchemaIds(ty => ty.FullName);
        //    // true : 设置控制器层注释，对控制器中方法的注释的显示
        //    // xml文档绝对路径,"WebApplication2"为项目名，表示读取该项目的xml文件
        //    //将上面代码放进去会报错，因为我们设置了xml文档地址：开启方法》右键项目》属性》生成》输出》打开“生成包含api的文档的文件”之后就可以直接运行了。
        //    var x = typeof(HostMoudule).Assembly.GetName().Name;
        //    var file = Path.Combine(AppContext.BaseDirectory, $"{x}.xml");
        //    // xml文档绝对路径
        //    //var path = Path.Combine(AppContext.BaseDirectory, file);
        //    option.IncludeXmlComments(file, true);
        //    //加载别的程序集文档文件
        //    option.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{typeof(HttpApiMoudule).Assembly.GetName().Name}.xml"), true);
        //    // 对方法名的名称进行排序，首字母顺序。
        //    option.OrderActionsBy(o => o.RelativePath);
        //});

        //IConfiguration configuration = services.GetConfiguration();
        //带身份验证注册
        services.AddAbpSwaggerGenWithOAuth(configuration["AuthServer:Authority"],
            new Dictionary<string, string>()
            {
                    { "api" ,$"{typeof(HostBuilder).Assembly.GetName().Name}" }
            }
            , option =>
            {
                option.SwaggerDoc("v3",
                   new OpenApiInfo
                   {
                       Title = typeof(YDT_ABPvNext_ProjectsHttpApiHostModule).Assembly.GetName().Name + "哈哈哈", //组名字，右上角可以选择
                       Version = "v1",//版本
                       Description = $"接口文档说明，放这里 ",
                       Contact = new OpenApiContact()
                       {
                           Name = "zhangsan",//名字
                           Email = "xxx@qq.com",//邮箱
                           Url = null
                       }//通常这里都是放联系，地址的，一般不用
                   });
                //显示Abp自动api接口
                option.DocInclusionPredicate((x, y) => true);
                option.CustomSchemaIds(ty => ty.FullName);
                // true : 设置控制器层注释，对控制器中方法的注释的显示
                //option.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{typeof(YDT_ABPvNext_ProjectsHttpApiHostModule).Assembly.GetName().Name}.xml"), true);
                //加载别的程序集文档文件
                //option.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{typeof(YDT_ABPvNext_ProjectsHttpApiHostModule).Assembly.GetName().Name}.xml"), true);
                // 对方法名的名称进行排序，首字母顺序。
                option.OrderActionsBy(o => o.RelativePath);
            });
        #endregion

        //配置AspNetCoreMvc模块
        Configure<AbpAspNetCoreMvcOptions>(option =>
        {
            //自动APi控制器
            //加载项目程序集  
            option.ConventionalControllers.Create(typeof(YDT_ABPvNext_ProjectsHttpApiHostModule).Assembly, option =>
            {
                //修改自动api控制器的根路径(abp)  //配置改造
                option.RootPath = "projects";
            });
            //忽略手动api控制器
            option.ControllersToRemove.Add(typeof(ProductImageController));

            //开启全局自动验证dto ，验证参数是否符合规范  默认配置为true
            //关闭之后，可以使用IValidationEnabled接口类局部开启
            //局部验证 -> 全局验证 -> 自动api验证或者手动api验证（默认）
            option.AutoModelValidation = false;
        });

        ////配置Api控制器异常
        Configure<AbpExceptionHandlingOptions>(oprion =>
        {
            //显示具体异常信息
            oprion.SendExceptionsDetailsToClients = false;
            //不显示异常堆栈
            oprion.SendStackTraceToClients = false;
        });

        //配置Api控制器异常响应状态码
        Configure<AbpExceptionHttpStatusCodeOptions>(option =>
        {
            //异常编码与响应编码映射
            option.Map("-1", HttpStatusCode.TooManyRequests);
            option.Map("2", HttpStatusCode.InternalServerError);
            option.Map("3", HttpStatusCode.NotImplemented);
            option.Map("4", HttpStatusCode.BadRequest);
            option.Map("xxx", HttpStatusCode.OK);
        });

        ////取消所有的权限校验
        //context.Services.AddAlwaysAllowAuthorization();

        //配置应用权限模块前缀
        AbpPermissionManagementDbProperties.DbTablePrefix = "YDT_";

        //配置应用身份认证模块前缀
        AbpIdentityDbProperties.DbTablePrefix = "YDT_";

        //配置应用租户模块
        AbpTenantManagementDbProperties.DbTablePrefix = "YDT_";

        ////配置审计日志模块
        AbpAuditLoggingDbProperties.DbTablePrefix = "YDT_";
    }

    private void ConfigureAuthentication(ServiceConfigurationContext context)
    {
        //context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
        context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
        {
            options.IsDynamicClaimsEnabled = true;
        });
    }

    private void ConfigureBundles()
    {
        Configure<AbpBundlingOptions>(options =>
        {
            options.StyleBundles.Configure(
                LeptonXLiteThemeBundles.Styles.Global,
                bundle =>
                {
                    bundle.AddFiles("/global-styles.css");
                }
            );
        });
    }

    private void ConfigureUrls(IConfiguration configuration)
    {
        Configure<AppUrlOptions>(options =>
        {
            options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
            options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty<string>());

            options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"];
            options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password";
        });
    }

    private void ConfigureVirtualFileSystem(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();

        if (hostingEnvironment.IsDevelopment())
        {
            Configure<AbpVirtualFileSystemOptions>(options =>
            {
                options.FileSets.ReplaceEmbeddedByPhysical<YDT_ABPvNext_ProjectsDomainSharedModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}YDT_ABPvNext_Projects.Domain.Shared"));
                options.FileSets.ReplaceEmbeddedByPhysical<YDT_ABPvNext_ProjectsDomainModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}YDT_ABPvNext_Projects.Domain"));
                options.FileSets.ReplaceEmbeddedByPhysical<YDT_ABPvNext_ProjectsApplicationContractsModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}YDT_ABPvNext_Projects.Application.Contracts"));
                options.FileSets.ReplaceEmbeddedByPhysical<YDT_ABPvNext_ProjectsApplicationModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}YDT_ABPvNext_Projects.Application"));
            });
        }
    }

    private void ConfigureConventionalControllers()
    {
        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(YDT_ABPvNext_ProjectsApplicationModule).Assembly);
        });
    }

    private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddAbpSwaggerGenWithOAuth(
            configuration["AuthServer:Authority"]!,
            new Dictionary<string, string>
            {
                    {"YDT_ABPvNext_Projects", "YDT_ABPvNext_Projects API"}
            },
            options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo { Title = "YDT_ABPvNext_Projects API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);
                options.CustomSchemaIds(type => type.FullName);
            });
    }

    private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder =>
            {
                builder
                    .WithOrigins(configuration["App:CorsOrigins"]?
                        .Split(",", StringSplitOptions.RemoveEmptyEntries)
                        .Select(o => o.RemovePostFix("/"))
                        .ToArray() ?? Array.Empty<string>())
                    .WithAbpExposedHeaders()
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseAbpRequestLocalization();

        if (!env.IsDevelopment())
        {
            app.UseErrorPage();
        }

        app.UseCorrelationId();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseCors();
        app.UseAuthentication();
        //app.UseAbpOpenIddictValidation();

        if (MultiTenancyConsts.IsEnabled)
        {
            app.UseMultiTenancy();
        }
        app.UseUnitOfWork();
        app.UseDynamicClaims();
        app.UseAuthorization();

        app.UseSwagger();
        app.UseAbpSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v3/swagger.json", "YDT_ABPvNext_Projects API");

            var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
            c.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
            c.OAuthScopes("YDT_ABPvNext_Projects");
        });

        app.UseAuditing();
        app.UseAbpSerilogEnrichers();
        app.UseConfiguredEndpoints();
    }
}
